package org.ddd.specification.catalog.bean;

import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.ddd.specification.Specification;
import org.ddd.specification.catalog.AbstractSpecification;

/**
 * Perform check on a specific bean property. Whenever the property cannot be found,
 * or accessed succesfully, this specification will evaluate {@code false}.
 * 
 * @author Jeroen van Schagen
 * @since 23-02-2011
 *
 * @param <T> type of bean that contains our property
 * @param <P> type of property being checked
 */
public class PropertySpecification<T, P> extends AbstractSpecification<T> {
    private final String propertyName;
    private final Specification<P> delegate;

    /**
     * Construct a new {@link PropertySpecification}.
     * @param propertyName name of the property being checked, cannot be blank (<b>required</b>)
     * @param delegate delegate specification, used to check our property value (<b>required</b>)
     */
    public PropertySpecification(String propertyName, Specification<P> delegate) {
        super();
        if (StringUtils.isBlank(propertyName)) {
            throw new IllegalArgumentException("Property name cannot be blank.");
        }
        if (delegate == null) {
            throw new IllegalArgumentException("Delegate specification cannot be null.");
        }
        this.propertyName = propertyName;
        this.delegate = delegate;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public boolean isSatisfiedBy(T candidate) {
        boolean satisfied = false;
        if (candidate != null) {
            try {
                Object propertyValue = PropertyUtils.getProperty(candidate, propertyName);
                // Delegate the rule check to our property value and composed specification
                satisfied = delegate.isSatisfiedBy((P) propertyValue);
            } catch (NoSuchMethodException e) {
                // TODO: Describe mismatch
            } catch (IllegalAccessException e) {
                // TODO: Describe mismatch
            } catch (InvocationTargetException e) {
                // TODO: Describe mismatch
            }
        }
        return satisfied;
    }

}
